use std::time::{Duration, Instant};

use actix::prelude::*;
use actix_web_actors::ws;

use crate::{
    config,
    server::{self},
    ws::{WsEvent, WsRequest, WsResponse},
};

/// How often heartbeat pings are sent
// const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(3);

/// How long before lack of client response causes a timeout
// const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);

// pub enum Action {
//     SessionCreate,

// }

#[derive(Debug)]
pub struct WsChatSession {
    pub server_id: usize,
    //
    pub client_id: usize,

    /// unique session id
    pub session_id: usize,

    /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
    /// otherwise we drop connection.
    pub hb: Instant,

    /// joined cluster
    // pub namespace: String,

    /// peer name
    // pub name: Option<String>,

    /// Chat server
    pub addr: Addr<server::ChatServer>,

    /// Is closed connection?
    pub closed: bool,

    // pub ip: IpAddr,

    // RSA加密
    #[allow(dead_code)]
    pub id_rsa: (String, String),
}

impl WsChatSession {
    /// helper method that sends ping to client every 5 seconds (HEARTBEAT_INTERVAL).
    ///
    /// also this method checks heartbeats from client
    fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
        // log::debug!("Start heartbeat timer, NID=`{}`", self.id);

        let interval = Duration::from_secs(
            config::get_server_heartbeat()
                .interval
                .unwrap_or(config::DEFAULT_SERVER_HEARTBEAT_INTERVAL) as u64,
        );

        // 如果是debug模式，暂停会导致执行多次
        ctx.run_interval(interval, |act, ctx| {
            // check client heartbeats
            if act.closed {
                return;
            }

            let timeout = Duration::from_secs(
                config::get_server_heartbeat()
                    .timeout
                    .unwrap_or(config::DEFAULT_SERVER_HEARTBEAT_TIMEOUT) as u64,
            );
            if Instant::now().duration_since(act.hb) > timeout {
                act.closed = true;

                // notify chat server
                act.addr.do_send(server::Disconnect {
                    myid: act.server_id,
                    client_id: act.client_id,
                    closed: act.closed,
                    // ip: act.ip,
                    // namespace: act.namespace.clone(),
                    session_id: act.session_id,
                });

                // heartbeat timed out
                log::info!(
                    "The {} session has been aborted due to heartbeat timed out {:?}",
                    act.client_id,
                    timeout
                );

                // stop actor
                ctx.stop();

                // don't try to send a ping
                return;
            }

            // 定时触发内部心跳事件
            // if act.server_id == act.client_id {
            //     act.addr.do_send(server::Heartbeat);
            // }

            ctx.ping(b"");
        });

        log::debug!("The {} session heartbeat timer started", self.client_id);
    }
}

impl Actor for WsChatSession {
    type Context = ws::WebsocketContext<Self>;

    /// Method is called on actor start.
    /// We register ws session with ChatServer
    fn started(&mut self, ctx: &mut Self::Context) {
        // we'll start heartbeat process on session start.

        self.hb(ctx);

        // register self in chat server. `AsyncContext::wait` register
        // future within context, but context waits until this future resolves
        // before processing any other events.
        // HttpContext::state() is instance of WsChatSessionState, state is shared
        // across all routes within application
        let addr = ctx.address();
        self.addr
            .send(server::Connect {
                // myid: config::get_server_id(),
                client_id: self.client_id,
                addr: addr.recipient(),
                // ip: self.ip,
            })
            .into_actor(self)
            .then(|res, act, ctx| {
                match res {
                    Ok(res) => {
                        act.session_id = res;
                        // act.id_rsa =
                        // 回写加密
                        // ctx.text("abc");
                        let myid = config::get_server_id();
                        if act.client_id == myid {
                            // 要求客户端断开连接
                            let rsp = WsResponse::error(None, format!("[{}] A follower with the same server_id as this slave has connected to the leader or other follower", myid), None);
                            ctx.text(rsp.to_string());
                            ctx.stop();
                        }
                    }
                    // something is wrong with chat server
                    _ => ctx.stop(),
                }
                fut::ready(())
            })
            .wait(ctx);

        log::debug!("The {} session started", self.client_id);
    }

    fn stopping(&mut self, _: &mut Self::Context) -> Running {
        // notify chat server
        self.addr.do_send(server::Disconnect {
            myid: self.server_id,
            client_id: self.client_id,
            closed: self.closed,
            // ip: self.ip,
            session_id: self.session_id,
        });
        Running::Stop
    }
}

/// Handle messages from chat server, we simply send it to peer websocket
impl Handler<server::Message> for WsChatSession {
    type Result = ();

    fn handle(&mut self, msg: server::Message, ctx: &mut Self::Context) {
        ctx.text(msg.0);
    }
}

/// WebSocket message handler
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsChatSession {
    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
        // log::debug!("==>{:?}", msg);

        let msg = match msg {
            Err(_) => {
                ctx.stop();
                return;
            }
            Ok(msg) => msg,
        };

        // log::debug!("==>msg:{:?}", msg);

        match msg {
            ws::Message::Ping(msg) => {
                self.hb = Instant::now();
                ctx.pong(&msg);
            }
            ws::Message::Pong(_) => {
                self.hb = Instant::now();
            }
            ws::Message::Binary(mut _buf) => {
                // 前面2个字节作为事件，事件包含HB、LV、...
                // let evt = buf.split_off(2);

                // |   32bits | 16bits  | 16bits  | 32bits      | 64bits      | unlimited |
                // | 4 bytes  | 2 bytes | 2 bytes | 4 bytes     | 8 bytes     | unlimited |
                // | CHECKSUM |  EVENT  |  VESION | HEAD LENGTH | DATA LENGTH | DATA BODY |
                // +----------+---------+---------+-------------+-------------+-----------+

                // let rsp = WsResponse::error(WsEvent::Unknown, "Unexpected binary".to_owned(), None);
                // let bin: Bincode<WsResponse> = Bincode::from(a);
                // let rsp = WsResponse::error(WsEvent::Unknown, "Unexpected binary".to_owned(), None);
                // ctx.text(rsp.to_string());
                // let str = rsp.to_string();
                // ctx.binary(str.clone().as_bytes());
            }
            ws::Message::Close(reason) => {
                ctx.close(reason);
                ctx.stop();
            }
            ws::Message::Continuation(_) => {
                ctx.stop();
            }
            ws::Message::Nop => (),
            ws::Message::Text(text) => {
                let s = &String::from(text);
                if s.is_empty() {
                    let rsp = WsResponse::error(None, "Empty WsRequest".to_owned(), None);
                    ctx.text(rsp.to_string());
                    return;
                }

                let v = match WsRequest::from_str(s) {
                    Ok(v) => v,
                    Err(e) => {
                        let rsp = WsResponse::error(
                            None,
                            format!(
                                "ParseError: `{}` is not valid JSON on WsRequest, cause: {}",
                                s, e
                            ),
                            None,
                        );
                        ctx.text(rsp.to_string());
                        return;
                    }
                };

                match v.evt {
                    WsEvent::RV => {
                        // 通知所有节点清理投票信息
                        // 原理是清理投票结果，然后重新投票
                        log::info!("Received Event SWITCHOVER, Ask from {}", v.vcd.myid);
                        self.addr.do_send(server::ResetVote);
                        // let rsp = WsResponse::ok(Some(WsEvent::RV), "Reset Voting".to_owned(), None);
                        // ctx.text(rsp.to_string());
                    }
                    WsEvent::DS => {
                        // let vcd;
                        // {
                        //     let &(ref lock, ref _cvar) = &*shared::SHARE_GLOBAL_AREA_MUTEX_PAIR.clone();
                        //     let loc = lock.try_lock().unwrap();
                        //     vcd = loc.get_vcd();
                        // }
                        let rsp = WsResponse::ok(Some(v.evt), "OK".to_owned(), None);
                        ctx.text(rsp.to_string());
                    }
                    WsEvent::LV => {
                        // 投票选举
                        let data = server::Election {
                            // session_id: self.session_id,
                            vcd: v.vcd.clone(),
                            // timeout: false,
                            result: None,
                            
                        };
                        self.addr
                            .send(data)
                            .into_actor(self)
                            .then(move |res, _, ctx| {
                                if let Ok(vote) = res {
                                    let vcd = Some(vote.vcd.clone());
                                    if let Some(result) = vote.result {
                                        match result {
                                            server::VotingResult::Leader => {
                                                // 刚加入的节点,leader已经选出了,无需参选
                                                ctx.text(
                                                    WsResponse::ok(
                                                        Some(WsEvent::FA),
                                                        "Non-Voted".to_owned(),
                                                        vcd,
                                                    )
                                                    .to_string(),
                                                );
                                            }
                                            server::VotingResult::Ok => {
                                                // 投票成功
                                                ctx.text(
                                                    WsResponse::ok(
                                                        Some(v.evt),
                                                        "Voting Ok".to_owned(),
                                                        vcd,
                                                    )
                                                    .to_string(),
                                                );
                                            }
                                            server::VotingResult::Failed => {
                                                // 投票失败
                                                ctx.text(
                                                    WsResponse::failed(
                                                        Some(v.evt),
                                                        "Voting Failed".to_owned(),
                                                        vcd,
                                                    )
                                                    .to_string(),
                                                );
                                            }
                                        }
                                    }
                                } else {
                                    println!("Something is wrong")
                                }
                                fut::ready(())
                            })
                            .wait(ctx);
                    }

                    _ => {
                        // 未适配的类型
                        ctx.text(
                            WsResponse::failed(
                                Some(v.evt),
                                "Unsupported event".to_owned(),
                                Some(v.vcd),
                            )
                            .to_string(),
                        );
                        return;
                    }
                }
            }
        }
    }
}
